/* eslint-disable no-param-reassign */

import React, {
  useMemo, useRef, useCallback, useEffect, useState,
} from 'react';
import { observer } from 'mobx-react-lite';
import { runInAction } from 'mobx';
import { useFormatMessage } from '@choerodon/master';
import {
  Icon, TextField, Tree,
  Modal, Tooltip, Button, Dropdown, Form, Select,
} from 'choerodon-ui/pro';
import toUpper from 'lodash/toUpper';
import { useDebounceFn } from 'ahooks';
import ScrollContext from 'react-infinite-scroll-component';
import { mount as injectMount, get } from '@choerodon/inject';
import { usePipelineManageStore } from '../../stores';
import TreeItem from './TreeItem';
import ExecuteContent from './execute-content';
import CopyPipeline from './components/copy-pipeline';
import useClickOut from './hooks/useClickOut';
import filterImg from './imgs/filter.svg';
import './index.less';
import { usePipelineTreeStore } from './stores';

const TreeMenu = observer(() => {
  const {
    intl: { formatMessage },
    mainStore,
    prefixCls,
    treeDs,
    projectId,
    hasCtyunTree,
    intlPrefix,
  } = usePipelineManageStore();
  const {
    CopyPipelineDataSet,
    seletDs,
    searchDataSet,
  } = usePipelineTreeStore();
  const executeKey = Modal.key();
  const stopKey = Modal.key();
  const deleteKey = Modal.key();
  const format = useFormatMessage('c7ncd.pipelineManage');

  const bounds = useMemo(() => mainStore.getNavBounds, [mainStore.getNavBounds]);
  const [hidden, setHidden] = useState(true);
  const [showPoint, setShowPoint] = useState(false);

  /**
   * 展开节点的所有父节点
   * 通过Record内置的方法展开目标节点
   * 将展开节点缓存进传入的数组
   * @param record
   * @param expendedKeys
   */
  function expandParents(record, expendedKeys) {
    if (!record.isExpanded) {
      const { children, parent } = record;

      if ((children && children.length) || record.get('hasRecords')) {
        const key = record.get('key');
        expendedKeys.push(key);
        record.isExpanded = true;
      }

      if (parent && !parent.isExpanded) {
        expandParents(parent, expendedKeys);
      }
    }
  }

  const handleOverLayReset = async () => {
    searchDataSet?.current?.set('status', undefined);
    searchDataSet?.current?.set('enableFlag', undefined);
    handleSearch();
    setShowPoint(false);
  };

  const handleOverLaySearch = () => {
    if (searchDataSet?.current?.get('status') || searchDataSet?.current?.get('enableFlag')) {
      setShowPoint(true);
      handleSearch();
      setHidden(true);
    }
  };

  const handleSearch = async (value) => {
    const obj = searchDataSet?.toData()[0];
    const tempArray = [];
    for (const item in obj) {
      if (obj[item]) {
        tempArray.push(`${item}=${obj[item]}`);
      }
    }
    const paramsStr = tempArray.join('&');
    const expandedKeys = [];

    // NOTE: 让多个 action 只执行一次
    runInAction(async () => {
      mainStore.setTreeDataPage(1);
      mainStore.setSearchValue(paramsStr);
      mainStore.setLoadedKeys([]);
      handleExpanded([]);
      /**
       *
       * 如果在 DataSet 的 load 方法中对原始数据进行了修改
       * 那么就不能使用 ds.reset(); 进行重置，因为该方法是基于 originalData 的
       * 应该手动将各记录重置
       *
       * */
      treeDs.forEach((record) => {
        record.reset();

        /**
         * 未清除搜索值就刷新，Record会记录expand状态，导致上一步record.reset()失效
         * */
        record.isExpanded = false;
      });
      await treeDs.query();
      treeDs.forEach((treeRecord) => {
        const pipelineName = treeRecord.get('name');
        const appServiceName = treeRecord.get('appServiceName');
        const parentId = treeRecord.get('parentId');
        const id = parentId && treeRecord.get('viewId') ? treeRecord.get('viewId').toString() : null;
        if (paramsStr) {
          if (!parentId && (toUpper(pipelineName)?.indexOf(toUpper(paramsStr)) > -1
            || toUpper(appServiceName)?.indexOf(toUpper(paramsStr)) > -1)
          ) {
            expandParents(treeRecord, expandedKeys);
          } else if (parentId && toUpper(id)?.indexOf(toUpper(paramsStr)) > -1) {
            expandParents(treeRecord, expandedKeys);
          }
        }
      });
    });

    const uniqKeys = new Set(expandedKeys);
    handleExpanded([...uniqKeys]);
  };
  // const handleCtyunSelect = (value) => {
  //   mainStore.setCtyunValue(value);
  // };
  const { run: handelDebounceSearch } = useDebounceFn(handleSearch, {
    wait: 1000,
  });
  function handleExpanded(keys) {
    mainStore.setExpandedKeys(keys);
  }

  const loadMoreTreeData = async () => {
    const page = mainStore.getTreeDataPage;
    mainStore.setTreeDataPage(page + 1);
    treeDs.query();
  };

  const onLoadData = async ({ key, children }) => {
    if (!children) {
      await mainStore.loadRecordData({ projectId, key, treeDs });
    }
  };
  function handleCopy(record) {
    Modal.open({
      key: Modal.key(),
      title: '复制流水线',
      children: <CopyPipeline
        copyPipelineDataSet={CopyPipelineDataSet}
        seletDs={seletDs}
        record={record}
      />,
    });
  }

  function handleDelete(record, refresh) {
    const deletePipeline = async () => {
      const res = await treeDs.delete(record, false);
      if (res && res.success) {
        const { key } = mainStore.getSelectedMenu || {};
        if (key === record.get('key')) {
          mainStore.setSelectedMenu({});
        }
        refresh();
      }
    };
    Modal.open({
      key: deleteKey,
      title: formatMessage({ id: `${intlPrefix}.delete.title` }),
      children: formatMessage({ id: `${intlPrefix}.delete.des` }),
      okText: formatMessage({ id: 'delete' }),
      onOk: () => deletePipeline(record),
      movable: false,
    });
  }
  async function handleExecute(record, refresh) {
    try {
      await mainStore.checkLinkToGitlab(projectId, record.get('appServiceId'), 'CI_PIPELINE_NEW_PERFORM');
      Modal.open({
        key: executeKey,
        title: formatMessage({ id: `${intlPrefix}.execute` }),
        children: <ExecuteContent
          appServiceId={record.get('appServiceId')}
          appServiceName={record.get('appServiceName')}
          gitlabProjectId={record.get('gitlabProjectId')}
          pipelineId={record.get('id')}
          record={record}
          refresh={refresh}
          prefixCls={prefixCls}
          mainStore={mainStore}
        />,
        okText: formatMessage({ id: 'c7ncd.pipelineManage.Execution' }),
        movable: false,
      });
    } catch (e) {
      //
    }
  }
  function handleChangeActive(record, refresh) {
    async function changePipelineActive(type) {
      const res = await mainStore.changePipelineActive({ projectId, pipelineId: record.get('id'), type });
      if (res) {
        refresh();
      }
    }
    if (record.get('enabled')) {
      Modal.open({
        key: stopKey,
        title: formatMessage({ id: `${intlPrefix}.stop.title` }),
        children: formatMessage({ id: `${intlPrefix}.stop.des` }),
        okText: formatMessage({ id: 'boot.stop' }),
        onOk: () => changePipelineActive('disable'),
        movable: false,
      });
    } else {
      changePipelineActive('enable');
    }
  }
  function nodeCover({ record }) {
    const nodeProps = {
      title: <TreeItem
        record={record}
        search={mainStore.getSearchValue}
        handleExecute={(refresh) => handleExecute(record, refresh)}
        handleChangeActive={(refresh) => handleChangeActive(record, refresh)}
        handleCopy={() => handleCopy(record)}
        handleDelete={(refresh) => handleDelete(record, refresh)}

      />,
    };
    if (!record.get('hasRecords')) {
      nodeProps.isLeaf = true;
    }
    return nodeProps;
  }
  const handleCtyunSearch = (value) => {
    mainStore.setCtyunSearchValue(value);
  };
  const renderTree = () => {
    if (hasCtyunTree) {
      return (
        <div className={`${prefixCls}-ctyun-tree`}>
          {injectMount('configuration-devops-routes-pipeline-manage-tree', {
            searchValue: mainStore.getCtyunSearchValue,
            handleExecute,
            handleChangeActive,
            handleCopy,
            setCtyunSelectedMenu: mainStore.setCtyunSelectedMenu,
            selectedMenu: mainStore.getCtyunSelectedMenu,
            fresh: mainStore.getCtyunFresh,
            setCtyunEmpty: mainStore.setCtyunEmpty,
          })}
        </div>
      );
    }
    return (
      <ScrollContext
        className={`${prefixCls}-sidebar-scroll`}
        dataLength={treeDs.length}
        next={loadMoreTreeData}
        hasMore={mainStore.getTreeDataHasMore}
        loader={null}
        height="100%"
      >
        <Tree
          dataSet={treeDs}
        // renderer={nodeRenderer}
        // eslint-disable-next-line react/jsx-no-bind
          onExpand={handleExpanded}
          className={`${prefixCls}-sidebar-tree`}
          loadData={onLoadData}
        // eslint-disable-next-line react/jsx-no-bind
          treeNodeRenderer={nodeCover}
          loadedKeys={mainStore.getLoadedKeys}
        />
      </ScrollContext>
    );
  };

  const getOverlay = () => {
    const overLaywidth = bounds.width - 20;
    return (
      <div style={{ width: overLaywidth }} role="none" className={`${prefixCls}-sidebar-overlay`} onMouseDown={(e) => e.stopPropagation()} ref={containerRef}>
        <Form dataSet={searchDataSet}>
          <Select
            name="status"
            getPopupContainer={(node) => node.parentElement}
          />
          <Select name="enableFlag" getPopupContainer={(node) => node.parentElement} />
        </Form>
        <div className={`${prefixCls}-sidebar-overlay-container-button`}>
          <Button onClick={handleOverLayReset}>
            重置
          </Button>
          <Button color="primary" onClick={handleOverLaySearch}>
            搜索
          </Button>
        </div>
      </div>
    );
  };

  const handleClickOut = useCallback(() => {
    setHidden(true);
  }, []);

  const containerRef = useClickOut(handleClickOut);

  return (
    <nav style={bounds} className={`${prefixCls}-sidebar`}>
      {
        !hasCtyunTree && (
          <Dropdown
            overlay={getOverlay()}
            hidden={hidden}
            getPopupContainer={(node) => node.parentElement}
            trigger={['click']}
          >
            <Tooltip title={format({ id: 'Search' })}>
              <TextField
                dataSet={searchDataSet}
                className={`${prefixCls}-sidebar-search`}
                placeholder={format({ id: 'Search' })}
                name="searchParam"
                prefix={<Icon type="search" />}
                suffix={(
                  <div className="suffix-container">
                    <div className="line" />
                    <Button
                      onClick={(e) => {
                        e.nativeEvent.stopImmediatePropagation();
                        setHidden(!hidden);
                      }}
                    >
                      <img src={filterImg} alt="" />
                    </Button>
                    {
                      showPoint && <div className="point" />
                    }
                  </div>
                )}
                value={mainStore.getSearchValue}
                onChange={handelDebounceSearch}
                valueChangeAction="input"
              />
            </Tooltip>
          </Dropdown>
        )
      }
      {
        hasCtyunTree && (
          <TextField
            className={`${prefixCls}-sidebar-search`}
            placeholder={format({ id: 'Search' })}
            clearButton
            name="search"
            prefix={<Icon type="search" />}
            value={mainStore.getCtyunSearchValue}
            onChange={handleCtyunSearch}
            valueChangeAction="blur"
          />
        )
      }
      {/* {!treeDs.length && treeDs.status === 'ready' ? (
        <span className={`${prefixCls}-sidebar-empty`}>{formatMessage({ id: 'nodata' })}</span>
      ) : renderTree()} */}
      {renderTree()}
    </nav>
  );
});

export default TreeMenu;
